<html>
<head>
<title>Blobs</title>
<meta http-equiv="content-type" content="text/html; charset=ISO-8859-1">

<script id="screen_clamp_shader-vs" type="x-shader/x-vertex">
    attribute vec2 aVertexPosition;
    varying vec2 vTextureCoord;
    void main(void) {
        gl_Position = vec4(aVertexPosition,0.0, 1.0);
		vTextureCoord = aVertexPosition;
    }
</script>

<script id="screen_clamp_shader-fs" type="x-shader/x-fragment">
    precision mediump float;
    varying vec2 vTextureCoord;
    uniform mat4 mView;
	uniform vec4 mTimeBlendSmoothDisplace;
	uniform vec4 mBoxPosXYZ;
	uniform vec4 mBoxScaleXYZ;
	uniform vec4 mSpherePosXYZ;
	uniform vec4 mSphereScaleXYZ;
	
	#define EPSILON 0.001
	#define VIEW_LEN 40.0

	float DistanceToPlane( vec4 plane, vec3 pos )
	{
		return dot(plane.xyz, pos ) - plane.w;
	}
	float DistanceToSphere( vec3 center, float radius, vec3 pos )
	{
		return (length(center-pos) - radius);
	}
	float DistanceToTorus(vec3 center, vec3 innerOuterScale, vec3 pos )
	{
	  pos = pos-center;
	  vec2 q = vec2(length(pos.xz)-innerOuterScale.x,pos.y/innerOuterScale.z);
	  return length(q)-innerOuterScale.y;
	}
	float DistanceToAABB( vec3 center, vec3 extents, vec3 pos )
	{
		return length(max(abs(pos-center)-extents,0.0));
	}
	float Blend( float d1, float d2, float scale)
	{
		float dd = smoothstep(-scale,scale,d1-d2);
		return mix(d1,d2,dd);
	}
	float displacement(vec3 pos)
	{
		pos = pos *0.003;
		float distance = (sin(967.0*pos.x)+sin(971.0*pos.y+3.0)+sin(977.0*pos.z+5.0));
		return min(max(distance,0.0),0.2);
	}
	float Ship(in vec3 pos)
	{	
		float part0Distance = DistanceToTorus(mSpherePosXYZ.xyz, mSphereScaleXYZ.xyz, pos);
		float part1Distance = DistanceToAABB(mBoxPosXYZ.xyz, mBoxScaleXYZ.xyz, pos);
		float distance = Blend(part1Distance, part0Distance, mTimeBlendSmoothDisplace.y);
		return distance-displacement(pos)*mTimeBlendSmoothDisplace.w-mTimeBlendSmoothDisplace.z;
	}
	float DistanceAtPoint( in vec3 pos)
	{
		pos.x = abs(pos.x);
		return Ship(pos);
	}
	vec3 NormalAtPoint( vec3 pos)
	{
		vec3 n = vec3(0.0,0.0,0.0);
		vec3 offset = vec3 ( 0.01, 0.0, 0.0 );
		
		n.x = DistanceAtPoint ( pos + offset.xyz ) - DistanceAtPoint ( pos - offset.xyz );
		n.y = DistanceAtPoint ( pos + offset.yxz ) - DistanceAtPoint ( pos - offset.yxz );
		n.z = DistanceAtPoint ( pos + offset.yzx ) - DistanceAtPoint ( pos - offset.yzx );
		
		return normalize ( n );
	}
    void main(void) {
	
		float ScreenRatio = 3.0/4.0;
		float FOVlength = 2.0;
	
		vec3 rayDir =  mView[0].xyz * vTextureCoord.x;
		rayDir += mView[1].xyz * vTextureCoord.y * ScreenRatio;
		rayDir += mView[2].xyz * FOVlength;
		
		vec3 rayPosition = mView[3].xyz;
		rayDir = normalize ( rayDir );
		vec3 color = vec3(1.0,1.0,1.0);
		vec3 shipColor0 = vec3(0.0,1.0,0.0);
		vec3 shipColor1 = vec3(1.0,0.0,0.0);
		vec3 lightPos = vec3(10.0,10.0,10.0);
		
		float totalDistance = 0.0;
		for (int i=0; i < 32; i++)
		{
			float distance = DistanceAtPoint(rayPosition);
			//distance += totalDistance*0.001;
			distance =  max(distance,EPSILON*2.0);
			rayPosition += distance*rayDir;
			totalDistance += distance;
			
			if (totalDistance > VIEW_LEN || distance < EPSILON)
				break;
		}
		
		if (totalDistance < VIEW_LEN)
		{
			vec3 lightDir = normalize(lightPos-rayPosition);
			vec3 normal = NormalAtPoint(rayPosition);
			
			float distanceInNormalDir = max(DistanceAtPoint(rayPosition+normal*0.1)*10.0,0.0);
			float light = max(dot(lightDir,normal),0.3);
			
			vec3 displacePos = rayPosition;
			displacePos.x = abs(displacePos.x);
			float displace = sign(displacement(displacePos));
			vec3 displaceColor = shipColor0*displace+shipColor1*(1.0-displace);
			color = displaceColor*light;//*distanceInNormalDir;;
		}
		gl_FragColor = vec4(color,1.0);
    }
</script>


<script type="text/javascript">

    var gl;
    var screenClampShaderProgram;
    var squareVertexPositionBuffer;
	var startTime;
	var canvas;
	var mousePos = [0.0,0.0];
	var mouseDown = false;
    function initGL(canvas) {
        try {
            gl = canvas.getContext("experimental-webgl");
            gl.viewportWidth = canvas.width;
            gl.viewportHeight = canvas.height;
        } catch (e) {
        }
        if (!gl) {
            alert("Could not initialise WebGL, sorry :-(");
        }
    }

    function getShader(gl, id) {
        var shaderScript = document.getElementById(id);
        if (!shaderScript) {
            return null;
        }

        var str = "";
        var k = shaderScript.firstChild;
        while (k) {
            if (k.nodeType == 3) {
                str += k.textContent;
            }
            k = k.nextSibling;
        }

        var shader;
        if (shaderScript.type == "x-shader/x-fragment") {
            shader = gl.createShader(gl.FRAGMENT_SHADER);
        } else if (shaderScript.type == "x-shader/x-vertex") {
            shader = gl.createShader(gl.VERTEX_SHADER);
        } else {
            return null;
        }

        gl.shaderSource(shader, str);
        gl.compileShader(shader);

        if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
            alert(gl.getShaderInfoLog(shader));
            return null;
        }

        return shader;
    }

	function initScreenClampShader() {
        var fragmentShader = getShader(gl, "screen_clamp_shader-fs");
        var vertexShader = getShader(gl, "screen_clamp_shader-vs");

        screenClampShaderProgram = gl.createProgram();
        gl.attachShader(screenClampShaderProgram, vertexShader);
        gl.attachShader(screenClampShaderProgram, fragmentShader);
        gl.linkProgram(screenClampShaderProgram);

        if (!gl.getProgramParameter(screenClampShaderProgram, gl.LINK_STATUS)) {
            alert("Could not initialise shaders");
        }

        gl.useProgram(screenClampShaderProgram);

        screenClampShaderProgram.vertexPositionAttribute = gl.getAttribLocation(screenClampShaderProgram, "aVertexPosition");
        gl.enableVertexAttribArray(screenClampShaderProgram.vertexPositionAttribute);

        screenClampShaderProgram.mView = gl.getUniformLocation(screenClampShaderProgram, "mView");
		screenClampShaderProgram.mTimeBlendSmoothDisplace = gl.getUniformLocation(screenClampShaderProgram, "mTimeBlendSmoothDisplace");
		screenClampShaderProgram.mBoxPosXYZ = gl.getUniformLocation(screenClampShaderProgram, "mBoxPosXYZ");
		screenClampShaderProgram.mBoxScaleXYZ = gl.getUniformLocation(screenClampShaderProgram, "mBoxScaleXYZ");
		screenClampShaderProgram.mSpherePosXYZ = gl.getUniformLocation(screenClampShaderProgram, "mSpherePosXYZ");
		screenClampShaderProgram.mSphereScaleXYZ = gl.getUniformLocation(screenClampShaderProgram, "mSphereScaleXYZ");
	}

    function initSquareBuffer() {
        squareVertexPositionBuffer = gl.createBuffer();
        gl.bindBuffer(gl.ARRAY_BUFFER, squareVertexPositionBuffer);
        vertices = [
             1.0,  1.0,
            -1.0,  1.0,
             1.0, -1.0,
            -1.0, -1.0
        ];
        gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
        squareVertexPositionBuffer.itemSize = 2;
        squareVertexPositionBuffer.numItems = 4;
    }
	
	function updateScene() {
	}
	function dot(vec0,vec1)
	{
		return vec0[0]*vec1[0]+vec0[1]*vec1[1]+vec0[2]*vec1[2];
	}
	function sub(vec0,vec1)
	{
		return [vec0[0]-vec1[0],vec0[1]-vec1[1],vec0[2]-vec1[2]];
	}
	function normalize(vec)
	{
		var len = Math.sqrt(dot(vec,vec));
		return [vec[0]/len, vec[1]/len, vec[2]/len];
	}
	function cross(vec0,vec1)
	{
		var x = vec0[1]*vec1[2] - vec0[2]*vec1[1]; 
		var y = vec0[2]*vec1[0] - vec0[0]*vec1[2]; 
		var z = vec0[0]*vec1[1] - vec0[1]*vec1[0];
		return [x,y,z];
	}
	function lookAt(eye, at, up)
	{
		var zaxis = normalize(sub(at, eye));
		var xaxis = normalize(cross(up, zaxis));
		var yaxis = cross(zaxis, xaxis);	
		/*return [
		xaxis[0], yaxis[0], zaxis[0], 0,
		xaxis[1], yaxis[1], zaxis[1], 0,
		xaxis[2], yaxis[2], zaxis[2], 0,
		-dot(xaxis, eye), -dot(yaxis, eye), -dot(zaxis, eye), 1
		];*/
		// inverse
		return [
		xaxis[0], xaxis[1],xaxis[2], 0,
		yaxis[0], yaxis[1], yaxis[2], 0,
		zaxis[0], zaxis[1], zaxis[2], 0,
		eye[0], eye[1], eye[2], 1
		];
		
	}
	function drawScene() {
		
        gl.bindFramebuffer(gl.FRAMEBUFFER, null);
		gl.viewport(0, 0, gl.viewportWidth, gl.viewportHeight);
    	
		gl.clearColor(0.0, 0.0, 0.0, 0.0);
		gl.clearDepth(1.0);
        gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
		
		// setup variables
		var d=new Date();
		var t=(d.getTime()-startTime.getTime())/1000;
		viewMat = lookAt([Math.cos(mousePos[0]*0.5)*20,Math.sin(mousePos[0]*0.5)*20,mousePos[1]], [0,0,0],[0,0,1]);
		var mTimeBlendSmoothDisplace = [
			t, 
			document.getElementById("BlendInput").value*0.03, 
			document.getElementById("SmoothInput").value*0.01,
			document.getElementById("DisplaceInput").value*0.004]; 
		var mBoxPosXYZ = [
			document.getElementById("BoxPosXInput").value*0.05, 
			document.getElementById("BoxPosYInput").value*0.05,
			document.getElementById("BoxPosZInput").value*0.05,
			0.0]; 
		var mBoxScaleXYZ = [
			document.getElementById("BoxScaleXInput").value*0.03+0.2, 
			document.getElementById("BoxScaleYInput").value*0.03+0.2,
			document.getElementById("BoxScaleZInput").value*0.03+0.2,
			0.0];  
		var mSpherePosXYZ = [
			document.getElementById("SpherePosXInput").value*0.03, 
			document.getElementById("SpherePosYInput").value*0.03,
			document.getElementById("SpherePosZInput").value*0.03,
			0.0]; 
		var mSphereScale = [
			document.getElementById("SphereInnerInput").value*0.03+0.2,
			document.getElementById("SphereOuterInput").value*0.03+0.2,
			document.getElementById("SphereScaleInput").value*0.03+1.0,
			0.0]; 
			
		
        gl.useProgram(screenClampShaderProgram);
		
	    gl.uniformMatrix4fv(screenClampShaderProgram.mView, false,viewMat);
		gl.uniform4fv(screenClampShaderProgram.mTimeBlendSmoothDisplace, mTimeBlendSmoothDisplace);
		gl.uniform4fv(screenClampShaderProgram.mBoxPosXYZ, mBoxPosXYZ);
		gl.uniform4fv(screenClampShaderProgram.mBoxScaleXYZ, mBoxScaleXYZ);
		gl.uniform4fv(screenClampShaderProgram.mSpherePosXYZ, mSpherePosXYZ);
		gl.uniform4fv(screenClampShaderProgram.mSphereScaleXYZ, mSphereScale);
		
		gl.bindBuffer(gl.ARRAY_BUFFER, squareVertexPositionBuffer);
		gl.vertexAttribPointer(screenClampShaderProgram.vertexPositionAttribute, squareVertexPositionBuffer.itemSize, gl.FLOAT, false, 0, 0);
		gl.drawArrays(gl.TRIANGLE_STRIP, 0, squareVertexPositionBuffer.numItems);
    	
    }
	

    function tick() {
        requestAnimFrame(tick);
		updateScene();
        drawScene();
    }
	

    function handleMouseMove(event) {
		if (mouseDown)
		{
			mousePos[0] = -((event.clientX/canvas.width)*2.0-1.0)*10;
			mousePos[1] = ((event.clientY/canvas.height)*2.0-1.0)*10;
		}
	}
	function handleMouseDown(event) {
		mouseDown = true;
	}
	function handleMouseUp(event) {
		mouseDown = false;
	}
    function webGLStart() {
        canvas = document.getElementById("blob-canvas");
		
        canvas.onmousemove = handleMouseMove;
		canvas.onmousedown = handleMouseDown;
		canvas.onmouseup = handleMouseUp;
		
        initGL(canvas);
        initScreenClampShader();
        initSquareBuffer();
		startTime = new Date();
		tick();       
    }
	
	window.requestAnimFrame = (function() {
	  return window.requestAnimationFrame ||
			 window.webkitRequestAnimationFrame ||
			 window.mozRequestAnimationFrame ||
			 window.oRequestAnimationFrame ||
			 window.msRequestAnimationFrame ||
			 function(/* function FrameRequestCallback */ callback, /* DOMElement Element */ element) {
			   window.setTimeout(callback, 1000/60);
			 };
	})();


</script>
 <style type="text/css">  
        .divLeftSide {  
            border: solid 1px #000000;  
            position:absolute;
			 top:0;
			 left:0;
			 width:800px; 
        }  
		  .divRightSide {  
           border: solid 1px #000000;  
            position:absolute;
			 top:0;
			 right:0;
			 width:300px;
        }  
    </style>  
</head>
<body onload="webGLStart();">
	<div class="divLeftSide">
    <canvas id="blob-canvas" style="border: none;" width="800" height="600"></canvas>
	</div>
	<div  class="divRightSide">
	<input id="BlendInput" type="range" min="0" max="100" value="50" step="1" />BlendInput<br/>
	<input id="SmoothInput" type="range" min="0" max="100" value="50" step="1" />SmoothInput<br/>
	<input id="DisplaceInput" type="range" min="0" max="100" value="50" step="1" />DisplaceInput<br/>
	<input id="BoxPosXInput" type="range" min="0" max="100" value="50" step="1" />BoxPosXInput<br/>
	<input id="BoxPosYInput" type="range" min="0" max="100" value="50" step="1" />BoxPosYInput<br/>
	<input id="BoxPosZInput" type="range" min="0" max="100" value="50" step="1" />BoxPosZInput<br/>
	<input id="BoxScaleXInput" type="range" min="0" max="100" value="50" step="1" />BoxScaleXInput<br/>
	<input id="BoxScaleYInput" type="range" min="0" max="100" value="50" step="1" />BoxScaleYInput<br/>
	<input id="BoxScaleZInput" type="range" min="0" max="100" value="50" step="1" />BoxScaleZInput<br/>
	<input id="SpherePosXInput" type="range" min="0" max="100" value="50" step="1" />SpherePosXInput<br/>
	<input id="SpherePosYInput" type="range" min="0" max="100" value="50" step="1" />SpherePosYInput<br/>
	<input id="SpherePosZInput" type="range" min="0" max="100" value="50" step="1" />SpherePosZInput<br/>
	<input id="SphereInnerInput" type="range" min="0" max="100" value="50" step="1" />SphereInnerInput<br/>
	<input id="SphereOuterInput" type="range" min="0" max="100" value="50" step="1" />SphereOuterInput<br/>
	<input id="SphereScaleInput" type="range" min="0" max="100" value="50" step="1" />SphereScaleInput<br/>
	</div>
</body>
</html>
